﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Layers;

namespace Queen
{
    public partial class EightQueen : UserControl
    {
        InteractiveBuffer buffers;
        Rectangle rect;
        int chessWidth = -1;

        SolidBrush whiteBrush;
        SolidBrush blackBrush;

        // Calculate session
        static bool[] ok_Minus;
        static bool[] ok_Plus;
        static bool[] ok_Row;
        static bool[] ok_Col;
        // solution[i] = j mean queen put at q(i,j)
        // Note that i,j[0;7]
        static int[] solution;

        bool haveResult = false;
        Thread thread;
        int sleepTime;
        bool step = false;
        System.Windows.Forms.Timer refreshTimer;

        int defaultWidth = -1;

        Image queenIcon;

        public EightQueen()
        {
            InitializeComponent();

            refreshTimer = new System.Windows.Forms.Timer();
            refreshTimer.Interval = 600;
            refreshTimer.Tick += new EventHandler(refreshTimer_Tick);
        }
        protected override void  OnVisibleChanged(EventArgs e)
        {
            if(this.defaultWidth == -1)
                this.defaultWidth = this.Width;
        }

        protected override void OnLoad(EventArgs e)
        {
            
            /* Init variable */
            if (chessWidth == -1)
            {
                this.chessWidth = 8; // init defaut
            }
            // Two function must call after init chessWidth
            if (EightQueen.solution == null) // first init
            {
                this.InitSolution();
                this.InitChess();
            }

            /* Init for drawing */

            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            
            this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.whiteBrush = new SolidBrush(Color.White);
            this.blackBrush = new SolidBrush(Color.Orange);


            this.rect = new Rectangle(0, 0, this.Width, this.Height);
            this.buffers = new InteractiveBuffer(this, rect);
            this.buffers.AddLayer("background");
            this.buffers.AddLayer("top");
            this.buffers["background"].Func += new RepresetBuffer.DrawBufferHandle(DrawChessBackground);
            this.buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawQueens);

            this.sleepTime = 1000;
            

            string filePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
            filePath += "\\imgs\\queen.png";
            queenIcon = Bitmap.FromFile(filePath);

        }

        protected override void OnResize(EventArgs e)
        {
            // Recreate buffer
            if (this.buffers != null)
            {
                this.rect = new Rectangle(0, 0, this.Width, this.Height);
                this.buffers = new InteractiveBuffer(this, rect);
                this.buffers.AddLayer("background");
                this.buffers.AddLayer("top");
                this.buffers["background"].Func += new RepresetBuffer.DrawBufferHandle(DrawChessBackground);
                this.buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawQueens);
                this.buffers.RefreshContext();
                this.Refresh();
            }
        }
        
            
        

        void refreshTimer_Tick(object sender, EventArgs e)
        {
            this.Refresh();
        }

        void DrawChessBackground(Graphics g, Rectangle r)
        {
            int cellWidth = this.Width / this.chessWidth;
            for (int i = 0; i < this.chessWidth; i++)
            {
                for(int j = 0; j < this.chessWidth; j++)
                {
                    if ((i + j) % 2 == 0)
                    {
                        g.FillRectangle(this.whiteBrush, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                    }
                    else
                    {
                        g.FillRectangle(this.blackBrush, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                    }

                }
            }

        }

        void DrawQueens(Graphics g, Rectangle r)
        {
            int cellWidth = this.Width / this.chessWidth;
            int tempWidth = cellWidth - 4;
            for (int i = 0; i < this.chessWidth; i++)
            {
                // i, solution[i](j);
                g.DrawImage(this.queenIcon, new Rectangle(solution[i] * cellWidth + 2, i * cellWidth + 2, tempWidth, tempWidth));
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.haveResult == true)
            {
                return;
            }
            this.buffers.Render(e.Graphics);
        }

        private delegate void MakeRefreshCallBack();
        private void MakeRefresh()
        {
            if (this.InvokeRequired)
            {
                MakeRefreshCallBack d = new MakeRefreshCallBack(MakeRefresh);
                this.Invoke(d);
            }
            else
            {
                this.Refresh();
            }
        }


        #region Calculate

        private void TryRow(object intObject)
        {
            int i = ((IntObject)intObject).ValueI;
            int jValue = ((IntObject)intObject).ValueJ;
            for (int j = jValue; j < this.chessWidth; j++)
            {
                int d_flus = i + j;
                // Diagonal minus index
                int d_minus = i - j + this.chessWidth - 1;

                if (EightQueen.ok_Row[i] == false &&
                    EightQueen.ok_Col[j] == false &&
                    EightQueen.ok_Plus[d_flus] == false &&
                    EightQueen.ok_Minus[d_minus] == false)
                {
                    EightQueen.solution[i] = j;
                    EightQueen.ok_Row[i] = true;
                    EightQueen.ok_Col[j] = true;
                    EightQueen.ok_Plus[d_flus] = true;
                    EightQueen.ok_Minus[d_minus] = true;

                    if (this.step == true)
                    {
                        this.step = false;
                        while (step == false)
                        {
                            Thread.Sleep(100);
                        }
                    }
                    else
                    {
                        if (this.sleepTime != 0)
                        {
                            Thread.Sleep(this.sleepTime);
                        }
                    }

                    if (i < this.chessWidth )
                    {
                        i++;
                        if (i == this.chessWidth)
                        {
                            this.MakeRefresh();
                            // make a signal to beak.
                            this.haveResult = true;
                            // stop update chess.
                            this.refreshTimer.Stop();
                            return;
                        }
                        else
                        {
                            IntObject tempInt = new IntObject(i);
                            this.TryRow(tempInt);
                        }
                    }

                    if (this.haveResult == true)
                    {
                        break;
                    }

                    // Reset value for backtrack
                    i = ((IntObject)intObject).ValueI;
                    d_flus = i + j;
                    // Diagonal minus index
                    d_minus = i - j + this.chessWidth - 1;

                    // If have backtrack from below row
                    EightQueen.ok_Row[i] = false;
                    EightQueen.ok_Col[j] = false;
                    EightQueen.ok_Plus[d_flus] = false;
                    EightQueen.ok_Minus[d_minus] = false;
                    EightQueen.solution[i] = -1;

                    if (i == 0) // first row
                    {
                        j++;
                        if (j == this.chessWidth)
                        {
                            // Continue loop
                            j = 0;
                            continue;
                        }
                        else
                        {
                            j--;
                            continue;
                        }

                    }

                }
            }
        }

        public void ResetSimulate()
        {
            this.AbortSimulate();
            this.InitSolution();
            this.InitChess();
            this.Refresh();
            this.haveResult = false;
        }

        public void Step()
        {
            this.step = true;
        }

        public void AbortSimulate()
        {
            if (this.thread != null)
            {
                thread.Abort();
                this.refreshTimer.Stop();
                thread = null;
            }
        }

        public void StartSimulate()
        {
            if (thread == null)
            {
                // Reset variable
                this.InitSolution();
                this.InitChess();
                this.haveResult = false;
                // We random first position
                Random random = new Random((int)DateTime.Now.Ticks);
                int ran = random.Next(this.chessWidth);

                IntObject temp = new IntObject(0, ran);
                thread = new Thread(new ParameterizedThreadStart(this.TryRow));
                thread.Start(temp);
                this.refreshTimer.Start();
            }
        }

        private void InitSolution()
        {
            EightQueen.solution = new int[this.chessWidth];
            for (int i = 0; i < this.chessWidth; i++)
            {
                // mark init state for solution
                EightQueen.solution[i] = -1;
            }
        }

        private void InitChess()
        {
            EightQueen.ok_Minus = new bool[this.chessWidth * 2 - 1];
            EightQueen.ok_Plus = new bool[this.chessWidth * 2 - 1];
            EightQueen.ok_Row = new bool[this.chessWidth];
            EightQueen.ok_Col = new bool[this.chessWidth];
        }

        // function update for control in window mode
        public void UpdateWindow()
        {
            if (this.refreshTimer == null)
            {
                this.refreshTimer = new System.Windows.Forms.Timer();
                this.refreshTimer.Interval = 600;
                this.refreshTimer.Tick += new EventHandler(refreshTimer_Tick);
                this.refreshTimer.Start();
            }
            else
            {
                this.refreshTimer.Start();
            }
            
        }

        public void ChangeChessWidthForControl(int newWidth)
        {
            // Reset default value
            this.Width = this.defaultWidth;
            this.Height = this.defaultWidth;

            this.chessWidth = newWidth;
            ResetSimulate();
            // round Width
            int cellWidth = this.Width / this.chessWidth;
            this.Width = cellWidth * this.chessWidth;

            // round height
            this.Height = this.Width;

            // Reset buffer
            this.rect = new Rectangle(0, 0, this.Width, this.Height);
            this.buffers = new InteractiveBuffer(this, rect);
            this.buffers.AddLayer("background");
            this.buffers.AddLayer("top");
            this.buffers["background"].Func += new RepresetBuffer.DrawBufferHandle(DrawChessBackground);
            this.buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawQueens);

            this.Refresh();
        }

        public void ChangeChessWidthForWindow(int newWidth)
        {
            // Reset default value
            this.Width = this.defaultWidth;
            this.Height = this.defaultWidth;

            this.chessWidth = newWidth;
            // round Width
            int cellWidth = this.Width / this.chessWidth;
            this.Width = cellWidth * this.chessWidth;

            // round height
            this.Height = this.Width;

            // Reset buffer
            this.rect = new Rectangle(0, 0, this.Width, this.Height);
            this.buffers = new InteractiveBuffer(this, rect);
            this.buffers.AddLayer("background");
            this.buffers.AddLayer("top");
            this.buffers["background"].Func += new RepresetBuffer.DrawBufferHandle(DrawChessBackground);
            this.buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawQueens);

            this.Refresh();
        }
                

        #endregion

        #region Property
        public int Speed
        {
            set
            {
                this.sleepTime = value;
            }
        }

        public bool IsCompleted
        {
            get
            {
                return this.haveResult;
            }
        }

        public int ChessWidth
        {
            set
            {
                this.chessWidth = value;
            }
            get
            {
                return this.chessWidth;
            }
        }
        #endregion
    }

    class IntObject: Object
    {
        int i;
        int j;
        public IntObject(int valueI, int valueJ = 0)
        {
            this.i = valueI;
            this.j = valueJ;
        }

        public int ValueI
        {
            get
            {
                return this.i;
            }
            set
            {
                this.i = value;
            }
        }

        public int ValueJ
        {
            get
            {
                return this.j;
            }
            set
            {
                this.j = value;
            }


        }
    }
}
